package name.cyrax.red.jpv.impl.funcs;

import java.lang.reflect.Array;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumberAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAccessor;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.funcs.DoubleFold;
import name.cyrax.red.jpv.api.funcs.IntegerFold;
import name.cyrax.red.jpv.api.funcs.LongFold;
import name.cyrax.red.jpv.api.funcs.MapReduceFactory;
import name.cyrax.red.jpv.api.vectors.VectorWriter;

public final class MapReduce
{
	private MapReduce() {};
	
	public static void fold(final IntegerAdapter adapter, IntegerFold folder, final int start, final int end)
	{
		for (int i = start; i < end; i++)
		{
			folder.set(i, adapter.getInteger(i));  
		}
	}
	
	public static void fold(final LongAdapter adapter, LongFold folder, final int start, final int end)
	{
		for (int i = start; i < end; i++)
		{
			folder.set(i, adapter.getLong(i));  
		}
	}
	
	public static void fold(final DoubleAdapter adapter, DoubleFold folder, final int start, final int end)
	{
		for (int i = start; i < end; i++)
		{
			folder.set(i, adapter.getDouble(i));  
		}
	}	
	
	public static void map(final IntegerAdapter adapter, int[] result, final int start, final int end, final int arrayStart)
	{
		final int arrayShift = arrayStart - start;
		for (int i = start; i < end; i++)
		{
			result[arrayShift + i] = adapter.getInteger(i);  
		}
	}
	
	public static void map(final LongAdapter adapter, long[] result, final int start, final int end, final int arrayStart)
	{
		final int arrayShift = arrayStart - start;
		for (int i = start; i < end; i++)
		{
			result[arrayShift + i] = adapter.getLong(i);  
		}
	}
	
	public static void map(final DoubleAdapter adapter, double[] result, final int start, final int end, final int arrayStart)
	{
		final int arrayShift = arrayStart - start;
		for (int i = start; i < end; i++)
		{
			result[arrayShift + i] = adapter.getDouble(i);  
		}
	}
	
	public static <N extends Number> void map(final NumberAdapter<? extends N> adapter, N[] result, final int start, final int end, final int arrayStart)
	{
		final int arrayShift = arrayStart - start;
		for (int i = start; i < end; i++)
		{
			result[arrayShift + i] = adapter.getNumber(i);
		}
	}

	public static int[] map(final IntegerAdapter adapter)
	{
		final int s = adapter.size();
		int[] result = new int[s];
		map(adapter, result, 0, s, 0);
		return result;
	}
	
	public static long[] map(final LongAdapter adapter)
	{
		final int s = adapter.size();
		long[] result = new long[s];
		map(adapter, result, 0, s, 0);
		return result;
	}
	
	public static double[] map(final DoubleAdapter adapter)
	{
		final int s = adapter.size();
		double[] result = new double[s];
		map(adapter, result, 0, s, 0);
		return result;
	}

	public static void map(NumericAdapter adapter, final VectorWriter<?> writer)
	{
		adapter.detectType(new NumericAccessor<Object>() {

			@Override public Object asInteger(IntegerAdapter adapter)
			{
				int size = adapter.size();
				for (int i = 0; i < size; i++)
					writer.set(i, adapter.getInteger(i));
				return null;
			}

			@Override public Object asLong(LongAdapter adapter)
			{
				int size = adapter.size();
				for (int i = 0; i < size; i++)
					writer.set(i, adapter.getLong(i));
				return null;
			}

			@Override public Object asDouble(DoubleAdapter adapter)
			{
				int size = adapter.size();
				for (int i = 0; i < size; i++)
					writer.set(i, adapter.getDouble(i));
				return null;
			}
		});
	}
	
//	public static void map(final NumericAdapter adapter, double[] result)
//	{
//		final double[] r = result;
//		adapter.access(new NumericAccessor<Object>() {
//
//			@Override public Object asInteger(IntegerAdapter adapter)
//			{
//				map(adapter
//				// TODO Auto-generated method stub
//				return null;
//			}
//
//			@Override public Object asLong(LongAdapter adapter)
//			{
//				// TODO Auto-generated method stub
//				return null;
//			}
//
//			@Override public Object asDouble(DoubleAdapter adapter)
//			{
//				// TODO Auto-generated method stub
//				return null;
//			}
//		});
//	}
//
	public static <N extends Number> N[] map(final NumberAdapter<? extends N> adapter, Class<N> resultType)
	{
		final int s = adapter.size();
		@SuppressWarnings("unchecked") 
		N[] result = (N[]) Array.newInstance(resultType, s);
		map(adapter, result, 0, s, 0);
		return result;
	}
	
//	public static <T extends IntegerFold> T fold(final T folder, final int start, final int end)
//	{
//		final IntegerGetter adapter = folder.adapter();
//		prepare(adapter);
//		for (int i = start; i < end; i++)
//		{
//			folder.set(i, adapter.getInt(i));  
//		}
//		return folder;
//	}
//
//	public static <T extends LongFold> T fold(final T folder, final int start, final int end)
//	{
//		final LongGetter adapter = folder.adapter();
//		prepare(adapter);
//		for (int i = start; i < end; i++)
//		{
//			folder.set(i, adapter.getLong(i));  
//		}
//		return folder;
//	}
//	
//	public static <T extends DoubleFold> T fold(final T folder, final int start, final int end)
//	{
//		final DoubleGetter adapter = folder.adapter();
//		prepare(adapter);
//		for (int i = start; i < end; i++)
//		{
//			folder.set(i, adapter.getDouble(i));  
//		}
//		return folder;
//	}
	
	private static final MapReduceFactory INSTANCE = new MapReduceFactoryImpl();
	
	public static MapReduceFactory getInstance() {
		return INSTANCE;
	}
	
}

final class MapReduceFactoryImpl implements MapReduceFactory {

	@Override public void map(NumericAdapter adapter, VectorWriter<?> writer)
	{
		MapReduce.map(adapter, writer);
	}

	@Override public int[] map(IntegerAdapter adapter)
	{
		return MapReduce.map(adapter);
	}

	@Override public long[] map(LongAdapter adapter)
	{
		return MapReduce.map(adapter);
	}

	@Override public double[] map(DoubleAdapter adapter)
	{
		return MapReduce.map(adapter);
	}

}; 

